package com.game.service.socket;

import com.game.service.Main;
import com.game.service.app.*;
import com.game.service.app.get_score.*;
import com.game.service.beans.GameMessage;
import com.game.service.beans.Login;
import com.game.service.beans.UserInfo;
import com.game.service.beans.game.*;
import com.game.service.beans.livepk.CreateLivePK;
import com.game.service.beans.livepk.LivePKMgr;
import com.game.service.db.UserScoreDaoImpl;
import com.game.service.db.UserScoreUpdate;
import com.game.service.define.GameDefine;
import com.game.service.robot.RobotMgr;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.java_websocket.WebSocket;
import org.java_websocket.enums.ReadyState;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import java.io.IOException;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.game.service.app.Score.*;
import static com.game.service.app.Utils.LogDate;
import static com.game.service.app.get_score.SaveUserScore.DESC_SYSTEM_EXIT;
import static com.game.service.beans.game.TableInfo.TABLE_DEFAULT_ID;
import static com.game.service.db.UserScoreDaoImpl.*;
import static com.game.service.define.GameDefine.*;
import static com.game.service.define.RoomConfig.*;


public class SocketServer extends WebSocketServer {

    static SocketServer mKernel;

    private final HashMap<String, TableInfo> tableMap = new HashMap<>();

    public HashMap<String, TableInfo> getTableMap() {
        return tableMap;
    }

    private long tableIdIndex = 100000;

    /**
     * 房间内玩家信息 最后结算以此为基础
     */
    public HashMap<Integer, UserInfo> userMap = new HashMap<>();
    /**
     * 玩家websocket维护列表
     */
    public HashMap<Integer, WebSocket> webMap = new HashMap<>();


    private GameConfig gameConfig;
    private boolean isGamesWriteScore = false;

    public static SocketServer getKernel() {
        if (mKernel == null) {
            mKernel = new SocketServer(PORT);
            mKernel.start();
        }
        return mKernel;
    }


    LivePKMgr livePKMgr;

    public LivePKMgr getLivePKMgr() {
        return livePKMgr;
    }

    RobotMgr robotMgr;


    public RobotMgr getRobotMgr() {
        return robotMgr;
    }

    ExecutorService executor;

    private SocketServer(int port) {
        super(new InetSocketAddress(port));
        mKernel = this;
        initConfig();
        robotMgr = new RobotMgr();
        livePKMgr = new LivePKMgr();
        FileUtils.writeTxtToFile("游戏重启,服务器正常运行\n", LogDate(), LOG_NAME_ROOM);
        executor = Executors.newSingleThreadExecutor();
        onGameRun();
    }

    void sendImageHead(UserInfo playerInfo) {
        String imageUrl = playerInfo.getUserLoginInfo().getAvatar();
        if (imageUrl.isEmpty()) {
            return;
        }
        //1.获取玩家头像信息,先本地检查是否有缓存文件(压缩发送),若有则加载本地，无则去下载原图
        String imageName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1);

        //检查本地是否有此文件
        String imagePath = Utils.getImageHeadFilePath(USER_HEAD_IMAGE_PATH, imageName);
        if (imagePath.isEmpty()) {
            //TODO 去下载最新的
            LoadUserImageHead loadUserImageHead = getLoadUserImageHead(playerInfo);
            loadUserImageHead.loadImageHead(USER_HEAD_IMAGE_PATH, imageUrl);
        } else {
            //直接发送
            try {
                String base64Image = "data:image/jpg;base64," + Utils.convertToBase64(imagePath);
                sendPlayerImageHead(playerInfo, base64Image);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    String getImageHeadBase64(UserInfo playerInfo) {
        String imageUrl = playerInfo.getUserLoginInfo().getAvatar();
        if (imageUrl.isEmpty()) {
            return "";
        }
        //1.获取玩家头像信息,先本地检查是否有缓存文件(压缩发送),若有则加载本地，无则去下载原图
        String imageName = imageUrl.substring(imageUrl.lastIndexOf('/') + 1);
        //检查本地是否有此文件
        String imagePath = Utils.getImageHeadFilePath(USER_HEAD_IMAGE_PATH, imageName);
        if (!imagePath.isEmpty()) {
            try {
                return "data:image/jpg;base64," + Utils.convertToBase64(imagePath);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return "";
    }


    private LoadUserImageHead getLoadUserImageHead(UserInfo playerInfo) {
        LoadUserImageHead loadUserImageHead = new LoadUserImageHead();
        loadUserImageHead.addListener(new LoadUserImageHead.ILoadListener() {
            @Override
            public void loadSuccess(String imageBase64) {

                sendPlayerImageHead(playerInfo, imageBase64);
                System.out.println("加载头像成功");
            }

            @Override
            public void error(String e) {
                System.out.println("加载头像错误");
                sendPlayerImageHead(playerInfo, "");
            }
        });
        return loadUserImageHead;
    }


    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {

       /* // conn.send("Welcome to the server!"); // This method sends a message to the new client
        // String ip = conn.getRemoteSocketAddress().getAddress().getHostAddress();
        // int port = conn.getRemoteSocketAddress().getPort();
        //  System.out.println(ip + ":" + port + " ,entered the room!");*/
        int size = this.getConnections().size();
        System.out.println("连接数量=" + size);

        send(conn, GAME_MAIN, GAME_SUB_SOCKET_SUCCESS, null);
    }


    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {
        //  broadcast(conn + " has left the room!");
        System.out.println("onClose:" + " has left the room!" + code + reason);
        if (!isGamesWriteScore) {//系统未锁定写分，玩家可以自由退出
            webMap.forEach((key, value) -> {
                if (conn == value) {
                    UserInfo player = getUser(key);
                    if (player != null) {
                        //如果玩家主动离开,修改登录分数记录,发送服务器结算
                        onUserExitGame(player);
                    }
                }
            });
        }


    }

    private UserInfo getUser(int uid) {
        //判断是否加入房间
        return userMap.get(uid);
    }

    @Override
    public void onMessage(WebSocket conn, String message) {

        try {
            Gson gson = new Gson();
            GameMessage gameMessage = gson.fromJson(message, GameMessage.class);
            final int main = gameMessage.getMain();
            final int sub = gameMessage.getSub();

            switch (main) {
                case GAME_MAIN:
                    //猜大小游戏消息
                    onSub(conn, sub, message);
                    break;
                case 201:
                    //其他游戏预留
                    break;
                case 0:
                    if (sub == 0) {
                        send(conn, 0, 0, null);
                    }
                    break;
                default:
                    break;
            }

        } catch (Exception e) {
            System.out.println("GameRun()" + e);
            String error = "onMessage()" + e + "\n" + Arrays.toString(e.getStackTrace());
            FileUtils.writeTxtToFile(error, LogDate(), "err.txt");
        }


    }


    @Override
    public void onError(WebSocket conn, Exception e) {
        System.out.println("onError()" + e);
        String error = "onError()" + e + "\n" + Arrays.toString(e.getStackTrace());
        FileUtils.writeTxtToFile(error, LogDate(), "err.txt");


    }

    @Override
    public void onStart() {
        System.out.println("Server started!");
        setConnectionLostTimeout(0);
        setConnectionLostTimeout(100);

    }


    private void onSub(WebSocket conn, int sub, String json) {

        switch (sub) {
            case GAME_C_SUB_ENTER_ROOM:
                EnterRoom(conn, json);
                break;
            case GAME_C_SUB_MATCH_WEAPON:
                matchWeaponUpdate(json);
                break;
            case GAME_C_SUB_MATCH_UP:
                matchUp(json);
                break;
            case GAME_C_SUB_MATCH_HIT:
                matchUpHit(json);
                break;
            case GAME_C_SUB_REMATCH:
                rematch(json);
                break;
            case GAME_C_SUB_MATCH_AGAIN:
                matchAgain(json);
                break;
            case GAME_C_SUB_MATCH_CLOSE_TABLE:
                matchCloseTable(json);
                break;
            case GAME_C_SUB_MATCH_EXIT_TABLE:
                exitTable(json);
                break;
            case GAME_C_SUB_MATCH_HEAD_INFO:
                getMatchHeadInfo(json);
                break;
            case GAME_C_SUB_CREATE_LIVE_PK_MATCH:
                createLivePKMatch(json);
                break;
            case GAME_C_SUB_JOIN_LIVE_PK_MATCH:
                joinLivePKMatch(json);
                break;
            case GAME_C_SUB_VIEWING_LIVE_PK_MATCH:
                livePKMgr.viewingLivePKMatch(json);
                break;
            case GAME_C_SUB_SPECTATOR_ENTER:
                livePKMgr.loginSpectator(conn, json);
                break;
            default:
                break;
        }
    }

    private void createLivePKMatch(String json) {
        Type type = new TypeToken<GameMessage<CreateLivePK>>() {
        }.getType();//无敌好用

        GameMessage<CreateLivePK> data = new Gson().fromJson(json, type);
        CreateLivePK livePK = data.getCmd();
        createLivePKTable(livePK);

    }

    private void matchCloseTable(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用
        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();
        System.out.println("matchCloseTable=" + data);
        //TODO 此时服务器做出反应
        // 1.取消房间,设置玩家状态

        closeTable(matchUp.getTableId(), matchUp.getUid());


    }

    /**
     * @param tableId 桌子ID
     * @param uid     玩家主动关闭桌子
     */
    private void closeTable(String tableId, int uid) {
        TableInfo mTable = tableMap.get(tableId);
        mTable.setOnDestroyUid(uid);
        mTable.setTableStatus(TableInfo.TableState.Destroy.ordinal());//设置离开

        UserInfo userA = mTable.getPlayerInfoA();
        UserInfo userB = mTable.getPlayerInfoB();
        if (userA != null) {
            userA.reset();
        }
        if (userB != null) {
            userB.reset();
        }
        send(getConn(userA), GAME_MAIN, GAME_C_SUB_MATCH_CLOSE_TABLE_RETURN, mTable);
        send(getConn(userB), GAME_MAIN, GAME_C_SUB_MATCH_CLOSE_TABLE_RETURN, mTable);
    }

   /* public void matchOver(WebSocket conn, String json, Gson gson) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = gson.fromJson(json, type);
        MatchUp matchUp = data.getCmd();
        System.out.println("主动认输=" + data);
        //TODO 此时服务器做出反应
        // 1.结算分数,同步给对方
        TableInfo mTable = tableMap.get(matchUp.getTableId());

        if (mTable != null && mTable.getWinner() == TableInfo.Identity.Default.ordinal()) {
            //   gameOver(mTable);//主动认输

        }

    }*/

    public void matchAgain(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();//玩家发起再来一局
        //TODO 此时服务器做出反应
        // 1.检查分数
        // 2.玩家发起再来一局,同步给对方,表示准备好了再来一局

        if (CheckInfo(matchUp)) {
            return;
        }

        System.out.println("matchAgain=" + data);
        TableInfo mTable = tableMap.get(matchUp.getTableId());
        if (mTable != null) {
            mTable.setWinner(TableInfo.Identity.Default.ordinal());
            UserInfo playerA = mTable.getPlayerInfoA();
            UserInfo playerB = mTable.getPlayerInfoB();
            if (matchUp.getUid() == playerA.getUid()) {
                //决战到底，重新初始化对战信息
                playerA.setPrepare(UserInfo.PREPARED);//修改自己的状态为准备好了
                updatePlayerPos(playerA);//
                playerA.setHp(GameDefine.PLAYER_HP);
                Weapon weapon = new Weapon();
                weapon.arrowIndex = 0;
                weapon.angle = 0;
                weapon.force = 0;
                weapon.status = 0;
                playerA.setWeapon(weapon);//初始化A
                playerA.setLifes(mTable.getConfig().getMatchWinNumber());//设置生命数

            }
            if (matchUp.getUid() == playerB.getUid()) {
                //决战到底，重新初始化对战信息
                playerB.setPrepare(UserInfo.PREPARED);//修改自己的状态为准备好了
                updatePlayerPos(playerB);
                playerB.setHp(GameDefine.PLAYER_HP);
                Weapon weapon = new Weapon();
                weapon.arrowIndex = 0;
                weapon.angle = 180;
                weapon.force = 0;
                weapon.status = 0;
                playerB.setWeapon(weapon);//初始化B
                playerB.setLifes(mTable.getConfig().getMatchWinNumber());//设置生命数

            }

            if (mTable.getPlayerInfoA().getPrepare() == UserInfo.PREPARED && mTable.getPlayerInfoB().getPrepare() == UserInfo.PREPARED) {
                mTable.setWinner(TableInfo.Identity.Default.ordinal());
                //TODO 决战到底-如果两个都准备好了,扣钱，开始游戏。
                mTable.getPlayerInfoA().setScoreLower(TABLE_ENTRY_FEE);
                mTable.getPlayerInfoB().setScoreLower(TABLE_ENTRY_FEE);
                mTable.setMatchStartTime(System.currentTimeMillis());

                userScoreUpdateDB(mTable.getPlayerInfoA(), -TABLE_ENTRY_FEE, CHANGE_TYPE_FEE, mTable.getTableId());
                userScoreUpdateDB(mTable.getPlayerInfoB(), -TABLE_ENTRY_FEE, CHANGE_TYPE_FEE, mTable.getTableId());

                userScoreDB(mTable.getPlayerInfoA(), CHANGE_TYPE_FEE);
                userScoreDB(mTable.getPlayerInfoB(), CHANGE_TYPE_FEE);

            }


            System.out.printf("桌子ID:%s,玩家:%s准备好了", matchUp.getTableId(), matchUp.getUid());
            send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_AGAIN_RETURN, mTable);
            send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_AGAIN_RETURN, mTable);


        }

    }

    void matchUpHit(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();
        int hitTag = matchUp.getHitTag();
        //   System.out.println("matchUpHit=" + data);
        TableInfo mTable = tableMap.get(matchUp.getTableId());
        if (mTable != null && matchUp.getUid() != matchUp.getHitUserid()) {
            if (mTable.getPlayerInfoA().getUid() == matchUp.getHitUserid()) {
                updateUserInfo(hitTag, mTable, mTable.getPlayerInfoA());
            }
            if (mTable.getPlayerInfoB().getUid() == matchUp.getHitUserid()) {
                updateUserInfo(hitTag, mTable, mTable.getPlayerInfoB());
            }

        }
    }

    private void updateUserInfo(int hitTag, TableInfo mTable, UserInfo userInfo) {
        mTable.getPlayerInfoA().setLossHp(0);//每次都初始化
        mTable.getPlayerInfoB().setLossHp(0);

        int hp = userInfo.getHp();
        int lossHp = 0;
        switch (hitTag) {
            case HIT_HEAD:
                lossHp = 400;
                break;
            case HIT_CENTER:
                lossHp = 200;
                break;
            case HIT_FOOT:
                lossHp = 100;
                break;
            default:
                break;
        }
        if (hp - lossHp > 0) {//如果血条值大于0,直接更新血条
            userInfo.setHp(hp - lossHp);
            userInfo.setLossHp(lossHp);

        } else {

            int life = userInfo.getLifes() - 1;
            if (life == 0) {//游戏结束
                userInfo.setLifes(life);
                userInfo.setHp(hp - lossHp);
                userInfo.setLossHp(lossHp);
            } else {//减少生命数,并且设置血条为满初始化新的血条及位置信息
                killOnce(mTable, userInfo);

                userInfo.setLossHp(lossHp);
                userInfo.setLifes(life);
                userInfo.setHp(PLAYER_HP);
                Weapon weapon = new Weapon();
                weapon.arrowIndex = 0;
                weapon.angle = userInfo.getDefender() == 1 ? 0 : 180;
                weapon.force = 0;
                weapon.status = 0;
                userInfo.setWeapon(weapon);//初始化

            }
        }
        send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_HIT_RETURN, mTable);
        send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_HIT_RETURN, mTable);
        if (userInfo.getLifes() == 0) {
            gameOver(mTable);//生命值消耗完结束
        }
    }

    private void killOnce(TableInfo mTable, UserInfo userInfo) {
        KillOncePosition killOncePosition = new KillOncePosition();
        killOncePosition.setTableId(mTable.getTableId());
        if (userInfo.getDefender() == TableInfo.Identity.Left.ordinal()) {
            initPlayerPos(userInfo);//隐藏的位置
            killOncePosition.userAInit = userInfo.getuPos();
            updatePlayerPos(userInfo);//刷新位置，客户端收到后先落下再升起
            killOncePosition.userAUpdate = userInfo.getuPos();
            //另外一个玩家不更新
            killOncePosition.userBInit = mTable.getPlayerInfoB().getuPos();
            killOncePosition.userBUpdate = mTable.getPlayerInfoB().getuPos();

        }

        if (userInfo.getDefender() == TableInfo.Identity.Right.ordinal()) {
            killOncePosition.userAInit = mTable.getPlayerInfoA().getuPos();
            killOncePosition.userAUpdate = mTable.getPlayerInfoA().getuPos();

            initPlayerPos(userInfo);//隐藏的位置
            killOncePosition.userBInit = userInfo.getuPos();
            updatePlayerPos(userInfo);//刷新位置，客户端收到后先落下再升起
            killOncePosition.userBUpdate = userInfo.getuPos();
        }
        send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_KILL_ONCE_RETURN, killOncePosition);
        send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_KILL_ONCE_RETURN, killOncePosition);
        livePKMgr.sendToSpectatorMatchKillOnce(killOncePosition);
    }

    public void exitTable(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();
        System.out.printf("玩家-%s,离开桌子%s", matchUp.getUid(), matchUp.getTableId());

        //TODO 离开桌子
        exitTable(matchUp.getTableId(), matchUp.getUid());
    }

    public void exitTable(String tableId, int destroyId) {
        TableInfo mTable = tableMap.get(tableId);
        if (mTable != null && mTable.getTableStatus() != TableInfo.TableState.Destroy.ordinal()) {
            //只要有人离开则销毁桌子
            mTable.setTableStatus(TableInfo.TableState.Destroy.ordinal());
            mTable.setOnDestroyUid(destroyId);
            UserInfo userA = mTable.getPlayerInfoA();
            UserInfo userB = mTable.getPlayerInfoB();
            if (userA != null) {
                userA.reset();
            }
            if (userB != null) {
                userB.reset();
            }
            send(getConn(userA), GAME_MAIN, GAME_C_SUB_MATCH_EXIT_TABLE_RETURN, mTable);
            send(getConn(userB), GAME_MAIN, GAME_C_SUB_MATCH_EXIT_TABLE_RETURN, mTable);

        }
    }

    public void rematch(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();//玩家发起重新匹配

        //TODO 此时服务器做出反应
        // 1.先检查玩家分数是否够参赛最低分数
        // 2.先离开原有的桌子,重新加入其他的桌子,有则直接加入,没有则创建桌子。


        if (CheckInfo(matchUp)) {
            return;
        }

        TableInfo mTable = tableMap.get(matchUp.getTableId());

        if (mTable != null && mTable.getTableStatus() != TableInfo.TableState.Destroy.ordinal()) {
            //只要有人离开则销毁桌子
            mTable.setTableStatus(TableInfo.TableState.Destroy.ordinal());
            mTable.setOnDestroyUid(matchUp.getUid());
            mTable.getPlayerInfoA().reset();
            mTable.getPlayerInfoB().reset();
            System.out.printf("玩家-%s,离开桌子%s,桌子状态%s", matchUp.getUid(), matchUp.getTableId(), mTable.getTableStatus());
            send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_REMATCH_RETURN, mTable);
            send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_REMATCH_RETURN, mTable);
        }


        if (CheckInfo(matchUp)) return;


        UserInfo player = getUser(matchUp.getUid());
        player.setTableId(TABLE_DEFAULT_ID);//重置玩家的桌子id
        player.setDefender(TableInfo.Identity.Default.ordinal());//重置玩家的桌子座位信息

        boolean isJoin = joinMatch(matchUp.getUid());

        if (!isJoin) {//没有桌子则创建桌子,自己作房主(右边)
            createTable(matchUp);
        }


    }


    public void matchUp(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();//玩家发起匹配。
        //TODO 此时服务器做出反应
        //1.查询玩家分数是否大于,房间参赛费。


        if (CheckInfo(matchUp)) {
            return;
        }

        // 1.先检查房间是否有桌子,有则直接加入,没有则创建桌子。
        boolean isJoin = joinMatch(matchUp.getUid());
        if (!isJoin) {//没有桌子则创建桌子,自己作房主(右边)
            createTable(matchUp);
        }

    }

    public void getMatchHeadInfo(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();//玩家
        TableInfo tableInfo = tableMap.get(matchUp.getTableId());
        if (tableInfo != null) {
            livePKMgr.sendMatchPlayerImageHead(matchUp.getUid(), tableInfo);
        }


    }

    /**
     * 检查是否可以停止比赛
     *
     * @param matchUp 玩家信息
     * @return true 则停止
     */
    public boolean CheckInfo(MatchUp matchUp) {
        if (Main.isClose()) {
            JsonBean<String> jsonBean = new JsonBean<>();
            jsonBean.code = GAME_SERVER_CLOSE;
            jsonBean.msg = "房间即将关闭,不能组建比赛了...敬请谅解";
            sendErrorMsg(webMap.get(matchUp.getUid()), jsonBean);
            return true;
        }

        UserInfo player = getUser(matchUp.getUid());
        if (player == null) {
            return true;
        }
        if (player.getScore() < TABLE_MIN_LOGIN) {
            JsonBean<String> jsonBean = new JsonBean<>();
            jsonBean.code = GAME_C_USER_NO_MONEY;
            jsonBean.msg = "分数不足,请返回充值";
            sendErrorMsg(webMap.get(matchUp.getUid()), jsonBean);
            return true;
        }
        return false;
    }

    private void createTable(MatchUp matchUp) {
        TableInfo mTable = new TableInfo();//创建桌子
        tableIdIndex++;//tableIdIndexId累加
        String tableId = Utils.getDateTableId() + tableIdIndex;
        mTable.setTableId(tableId);
        mTable.setTableStatus(TableInfo.TableState.ACreate.ordinal()); //默认创建为0
        mTable.setWinner(TableInfo.Identity.Default.ordinal());//默认0
        mTable.setMatchDuration(MATCH_DURATION);//默认60
        mTable.setOnDestroyTime(DESTROY_TIME);//默认30
        TableConfig config = new TableConfig();
        config.setMatchWinNumber(MATCH_WIN_NUMBER);
        config.setMatchDuration(MATCH_DURATION);
        mTable.setConfig(config);


        UserInfo user = getUser(matchUp.getUid());//添加房主
        user.setDefender(TableInfo.Identity.Left.ordinal());//设置为房主
        initUser(user, mTable);
        mTable.setPlayerInfoA(user);//添加房主
        tableMap.put(tableId, mTable);//添加房间
        send(getConn(user), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
        System.out.println("创建桌子=" + mTable);
    }


    /**
     * 玩家游戏途中退出送礼,再次回到游戏,需要重新获取分数,刷新分数
     */
    private void UpdateUserScore(Login login, UserInfo user) {

        FileUtils.writeTxtToFile("进入房间,玩家进入信息" + login, LogDate(), getFileName(login.getUid()));

        if (login.getLiveid() == 0) {
            FileUtils.writeTxtToFile("进入房间,玩家信息异常" + login, LogDate(), getFileName(login.getUid()));
            return;
        }
        SaveUserScore saveUserScore = new SaveUserScore();
        saveUserScore.addListener(new SaveUserScore.ISendListener() {
            @Override
            public void sendSuccess() {
                new Thread(() -> {

                    String json0 = new Gson().toJson(login);
                    System.out.println(FileUtils.getTime() + ",请求地址" + API_GET_COIN + "数据" + json0);

                    String result = doPostForm(API_GET_COIN, json0);
                    if (result.isEmpty()) {
                        FileUtils.writeTxtToFile("进入房间,获取账户分数失败" + login, LogDate(), getFileName(login.getUid()));
                        JsonBean<String> cmd = new JsonBean<>();
                        cmd.code = 1;
                        cmd.msg = "Failed to obtain score";
                        sendErrorMsg(webMap.get(login.getUid()), cmd);//发送错误UpdateUserScore
                        return;
                    }

                    Type type0 = new TypeToken<JsonBean<UserLoginInfo>>() {
                    }.getType();

                    JsonBean<UserLoginInfo> bean = new Gson().fromJson(result, type0);
                    final UserLoginInfo userLoginInfo = bean.data;
                    System.out.println(FileUtils.getTime() + bean);
                    FileUtils.writeTxtToFile("获取分数," + bean, LogDate(), getFileName(login.getUid()));

                    if (bean.code == 0) {
                        //则修改在线状态
                        user.setStatus(USER_STATUS_ONLINE);
                        Money moneyInit = user.getMoney();
                        user.getMoney().setCoin(moneyInit.getCoin() + userLoginInfo.getCoin());//初始金币+获取金币
                        user.getMoney().setDiamond(moneyInit.getDiamond() + userLoginInfo.getDiamond());//初始钻石+获取钻石
                        Money moneyLast = user.getMoney();
                        user.setScore(getScore(moneyLast));
                        user.setName(userLoginInfo.getUser_nickname());
                        user.setUserLoginInfo(userLoginInfo);

                        //TODO 保存玩家登录分数,防止服务器异常关闭导致分数丢失
                        final String jsonWriteScore = userScoreUpdateJson(user);
                        saveUserScore.saveUserScore(user.getUid() + "", jsonWriteScore, API_POST_WriteScore, CHANGE_TYPE_LOGIN_OFFLINE);
                        userScoreUpdateDB(user, getScore(moneyLast), CHANGE_TYPE_LOGIN_OFFLINE, "ArrowPlaza");

                        send(webMap.get(login.getUid()), GAME_MAIN, GAME_C_SUB_ENTER_ROOM_RETURN, user);
                        sendMatchLivePkInfo(login);
                        sendMatchInfo(login, user);
                    }

                }).start();
            }

            @Override
            public void sendError() {

            }
        });
        saveUserScore.checkDBUserScore(user.getUid() + "");

    }

    private String userScoreUpdateJson(UserInfo user) {
        GamesWriteScore writeScore = getGamesWriteScore(user);
        ArrayList<GamesWriteScore> writeScoreList = new ArrayList<>();
        writeScoreList.add(writeScore);
        return getJsonWriteScore(writeScoreList);
    }


    private void LeaveRoom(WebSocket conn, String json, Gson gson) {

        Type type = new TypeToken<GameMessage<Login>>() {
        }.getType();//无敌好用
        GameMessage<Login> data = gson.fromJson(json, type);
        int id = data.getCmd().getUid();
        webMap.remove(id);//玩家离开房间，移除连接保存
        // userMap.get(id).setStatus(USER_STATUS_EXIT);
    }

    private void EnterRoom(WebSocket conn, String json) {

        //玩家进入房间
        Type type = new TypeToken<GameMessage<Login>>() {
        }.getType();//无敌好用
        GameMessage<Login> data = new Gson().fromJson(json, type);
        Login login = data.getCmd();
        if (Main.isClose()) {//点击关闭后,禁止玩家登录
            return;
        }
        final int uid = login.getUid();
        webMap.put(uid, conn);
        UserInfo playerInfo = getUser(uid);
        if (playerInfo == null) {
            GetScoreFirst(conn, json);
            return;
        }
        //玩家中途退出，再次进来需要重新获取分数及其他信息
        if (playerInfo.getStatus() == USER_STATUS_EXIT) {
            UpdateUserScore(login, playerInfo);
        }


    }

    /**
     * 更新用户的登录信息
     *
     * @param login 登录信息
     * @param user  用户信息
     */

    private void sendMatchInfo(Login login, UserInfo user) {
        if (login.getIsLoadImageHead() == 0) {
            //玩家首次进入,发送头像，检查是否有比赛
            sendImageHead(user);

            TableInfo mTable = tableMap.get(user.getTableId());
            if (mTable != null && mTable.getTableStatus() != TableInfo.TableState.Destroy.ordinal()) {
                //如果检查还有比赛未完成,
                if (mTable.getMatchDuration() < MATCH_DURATION && mTable.getMatchDuration() > 0) {
                    sendMatchPlayerImageHead(mTable);   //单独发送头像图片

                }
                send(getConn(user), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
            }
        }
    }


    public void matchWeaponUpdate(MatchWeapon matchWeapon) {

        TableInfo mTable = tableMap.get(matchWeapon.getTableId());
        Weapon weapon = matchWeapon.getWeapon();

        if (mTable != null) {
            if (matchWeapon.getUid() == mTable.getPlayerInfoA().getUid()) {
                UserInfo playerInfoA = mTable.getPlayerInfoA();
                updateWeapon(playerInfoA.getWeapon(), weapon);
                if (weapon.status == 1) {
                    playerInfoA.getWeapon().arrowIndex += 1;
                    if (playerInfoA.getWeapon().arrowIndex >= 3) {
                        playerInfoA.getWeapon().arrowIndex = 0;
                    }
                }
                mTable.setPlayerInfoA(playerInfoA);
            }
            if (matchWeapon.getUid() == mTable.getPlayerInfoB().getUid()) {
                UserInfo playerInfoB = mTable.getPlayerInfoB();
                updateWeapon(playerInfoB.getWeapon(), weapon);
                if (weapon.status == 1) {
                    playerInfoB.getWeapon().arrowIndex += 1;
                    if (playerInfoB.getWeapon().arrowIndex >= 3) {
                        playerInfoB.getWeapon().arrowIndex = 0;
                    }
                }
                mTable.setPlayerInfoB(playerInfoB);
            }
            send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_WEAPON_RETURN, mTable);
            send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_WEAPON_RETURN, mTable);
            livePKMgr.sendToSpectatorWeapon(mTable);
            //重置未未发射状态
            if (mTable.getPlayerInfoA().getWeapon() != null) {
                if (mTable.getPlayerInfoA().getWeapon().status == 1) {
                    mTable.getPlayerInfoA().getWeapon().force = 0;
                }
                mTable.getPlayerInfoA().getWeapon().status = 0;

            }
            if (mTable.getPlayerInfoB().getWeapon() != null) {
                if (mTable.getPlayerInfoB().getWeapon().status == 1) {
                    mTable.getPlayerInfoB().getWeapon().force = 0;
                }
                mTable.getPlayerInfoB().getWeapon().status = 0;

            }
            // System.out.println(FileUtils.getTime() + mTable);
        }


    }

    public void matchWeaponUpdate(String json) {

        Type type = new TypeToken<GameMessage<MatchWeapon>>() {
        }.getType();//无敌好用

        GameMessage<MatchWeapon> data = new Gson().fromJson(json, type);
        MatchWeapon userWeapon = data.getCmd();
        matchWeaponUpdate(userWeapon);

    }


    public void send(WebSocket conn, int main, int sub, Object cmd) {

        if (conn == null) {

            return;
        }
        Gson gson = new Gson();
        GameMessage gameMessage = new GameMessage();
        gameMessage.setMain(main);
        gameMessage.setSub(sub);
        gameMessage.setCmd(cmd);
        String json = gson.toJson(gameMessage);
        if (conn.getReadyState() == ReadyState.OPEN) {
            conn.send(json);
        }
        robotMgr.send(main, sub, cmd);
        livePKMgr.send(main, sub, cmd);
        // System.out.println(conn.getReadyState()+","+main+"-"+sub);
    }

    /***
     * 转发类型的消息
     * @param conn 连接
     * @param main 主命令
     * @param sub 次命令
     * @param cmd 发送对象
     */
    public void sendFF(WebSocket conn, int main, int sub, Object cmd) {

        if (conn == null) {

            return;
        }
        Gson gson = new Gson();
        GameMessage gameMessage = new GameMessage();
        gameMessage.setMain(main);
        gameMessage.setSub(sub);
        gameMessage.setCmd(cmd);
        String json = gson.toJson(gameMessage);
        if (conn.getReadyState() == ReadyState.OPEN) {
            conn.send(json);
        }
        // System.out.println(conn.getReadyState()+","+main+"-"+sub);
    }


    private int runTime = 0;


    /**
     * 服务器开启成功后运行游戏线程
     */
    public void onGameRun() {
        CountdownTimer countdown = new CountdownTimer(); //
        TimerTask task = new TimerTask() {

            public void run() {

                try {
                    MyGameRun();
                } catch (Exception e) {
                    e.printStackTrace();
                    String error = "GameRun()" + e + "\n" + Arrays.toString(e.getStackTrace());
                    System.out.println("GameRun()" + e);
                    FileUtils.writeTxtToFile(error, LogDate(), "err.txt");
                }

            }

            private void MyGameRun() {

                int tableNum = 0;
                for (Map.Entry<String, TableInfo> entry : tableMap.entrySet()) {
                    TableInfo mTable = entry.getValue();
                    UserInfo userA = mTable.getPlayerInfoA();
                    UserInfo userB = mTable.getPlayerInfoB();

                    boolean ACreate = mTable.getTableStatus() == TableInfo.TableState.ACreate.ordinal();
                    boolean bBJoin = mTable.getTableStatus() == TableInfo.TableState.BJoin.ordinal();
                    boolean bDestroy = mTable.getTableStatus() == TableInfo.TableState.Destroy.ordinal();

                    boolean bPrepareA = userA != null && userA.getPrepare() == UserInfo.PREPARED;
                    boolean bPrepareB = userB != null && userB.getPrepare() == UserInfo.PREPARED;

                    //房间里玩家都准备了,开始比赛倒计时
                    if (bBJoin && bPrepareA && bPrepareB) {//如果都准备了,并且房间状态是BJoin
                        ++tableNum;//统计正在比赛的桌子数量
                        mTable.setCreateTime(0);
                        mTable.setOnDestroyTime(DESTROY_TIME);
                        mTable.setMatchDurationLower();
                        boolean bAllowSettlement = mTable.getWinner() == TableInfo.Identity.Default.ordinal();//是否允许结算
                        if (mTable.getMatchDuration() == 0 && bAllowSettlement) {
                            //TODO 如果剩余时间小于0,并且当前房间未结算，(自动结算)游戏结束。
                            gameOver(mTable);//自动结束
                            return;
                        }
                        livePKMgr.updateSpectators(mTable);
                        send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
                        send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
                        //System.out.println("mTable.getJoinTime(): " + mTable + mTable.getMatchDuration() + " 秒");
                    }


                    //如果房间有两个玩家,30秒内玩家不准备则销毁房间
                    if (bBJoin && (!bPrepareA || !bPrepareB)) {
                        mTable.setOnDestroyTimeLower();
                        if (mTable.getOnDestroyTime() == 0) {
                            closeTable(mTable);//游戏完30秒不准备，销毁房间
                        }
                        //  System.out.println("mTable.getOnDestroyTime(): " + mTable.getOnDestroyTime() + " 秒");
                    }
                    //房间里还未结算时,开始显示匹配时间
                    if (ACreate) {//房间创建
                        mTable.setCreateTimeAdd();
                        mTable.setOnDestroyTime(DESTROY_TIME);
                        send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
                        send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
                        //  System.out.println("mTable.getCreateTime(): " + mTable.getCreateTime() + " 秒");
                        if (mTable.getCreateTime() >= MATCH_MAX_TIME) {
                            //最大匹配时间2分钟，防止有些玩家匹配了出去离开界面
                            closeTable(mTable);
                        }


                    }

                    //如果管理员准备关闭房间,游戏没有开始的房间直接关闭
                    if (Main.isClose() && mTable.getMatchDuration() == MATCH_DURATION) {
                        closeTable(mTable);
                    }
                }
                tableMap.entrySet().removeIf(item -> tableDestroy(item.getValue()));

/*//                for (Iterator<Map.Entry<Long, TableInfo>> it = tableInfoHashMap.entrySet().iterator(); it.hasNext(); ) {
//                    Map.Entry<Long, TableInfo> item = it.next();
//                    if (item.getValue().getTableStatus() == TableInfo.TableState.Destroy.ordinal()) {
//                        it.remove();
//                    }
//                }*/


                if (Main.isClose() && tableNum == 0) {//表示没有比赛房间,结算所有的玩家，并且退出
                    onSystemExitGame();//只能发送一次，
                }

                //     System.out.println("运行时间: " + runTime + " 秒" + ",正在比赛的桌子数量=" + tableNum);

                runTime++;
                if (runTime % 60 == 0) {
                    //处理未发送成功的订单
                    PostJsonDealFailed.getInstance().dealFailJson();
                    //加载新的游戏配置
                    initConfig();
                }

            }
        };
        countdown.startTimer(task);
    }

    private boolean tableDestroy(TableInfo mTable) {
        boolean haveRemove = mTable.getTableStatus() == TableInfo.TableState.Destroy.ordinal();
        if (haveRemove) {
            livePKMgr.getImageHeadMap().remove(mTable.getTableId());
        }
        return haveRemove;
    }

    /***
     *  系统关闭桌子
     * @param mTable 桌子信息
     */

    private void closeTable(TableInfo mTable) {
        mTable.setTableStatus(TableInfo.TableState.Destroy.ordinal());
        mTable.setOnDestroyUid(ADMIN_ID);//系统关闭房间
        UserInfo userA = mTable.getPlayerInfoA();
        UserInfo userB = mTable.getPlayerInfoB();
        if (userA != null) {
            userA.reset();
        }
        if (userB != null) {
            userB.reset();
        }
        send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
        send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_UP_RETURN, mTable);
    }

    /***
     * 相同血量下,房主胜利
     * @param mTable 桌子信息
     */
    private void gameOver(TableInfo mTable) {
        //写分后重置数据
        mTable.getPlayerInfoA().setPrepare(UserInfo.UNPREPARED);//设置未准备
        mTable.getPlayerInfoB().setPrepare(UserInfo.UNPREPARED);
        mTable.setMatchDuration(MATCH_DURATION);//设置比赛时间
        mTable.setOnDestroyTime(DESTROY_TIME);//设置销毁时间

        initPlayerPos(mTable.getPlayerInfoA());
        initPlayerPos(mTable.getPlayerInfoB());


        //生命值领先胜利
        boolean lifeWin = mTable.getPlayerInfoA().getLifes() > mTable.getPlayerInfoB().getLifes();
        if (lifeWin) {
            winA(mTable);
        }
        boolean lifeEqual = mTable.getPlayerInfoA().getLifes() == mTable.getPlayerInfoB().getLifes();
        //生命值相等时
        if (lifeEqual) {
            boolean hpWin = mTable.getPlayerInfoA().getHp() >= mTable.getPlayerInfoB().getHp();
            //血量多者胜利
            if (hpWin) {
                winA(mTable);
            } else {
                winB(mTable);
            }
        }
        boolean right = mTable.getPlayerInfoA().getLifes() < mTable.getPlayerInfoB().getLifes();
        if (right) {
            winB(mTable);
        }
        System.out.printf("桌子ID:%s,游戏结束", mTable.getTableId());
        //TODO 此处发送游玩记录,
        PostPlaying(mTable);


        send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_OVER_RETURN, mTable);
        send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_OVER_RETURN, mTable);
    }

    private void winA(TableInfo mTable) {

        float commission = getCommission(mTable.getPlayerInfoA());
        long commissionLive;
        long win;
        if (mTable.getLivePK() != null) {
            commissionLive = (long) (mTable.getConfig().getMatchEnterFee() * commission);
            win = (long) (mTable.getConfig().getMatchEnterFee() * (2 - commission));
        } else {
            commissionLive = (long) (TABLE_ENTRY_FEE * commission);
            win = (long) (TABLE_ENTRY_FEE * (2 - commission));
        }

        mTable.setWinner(TableInfo.Identity.Left.ordinal());//设置左边胜利
        mTable.getPlayerInfoA().setScoreAdd(win);
        mTable.getPlayerInfoA().setCommissionAdd(commissionLive);
        userScoreUpdateDB(mTable.getPlayerInfoA(), win, UserScoreDaoImpl.CHANGE_TYPE_WIN, mTable.getTableId());
        userScoreDB(mTable.getPlayerInfoA(), CHANGE_TYPE_WIN);

    }

    private void winB(TableInfo mTable) {
        float commission = getCommission(mTable.getPlayerInfoB());
        long commissionLive;
        long win;
        if (mTable.getLivePK() != null) {
            commissionLive = (long) (mTable.getConfig().getMatchEnterFee() * commission);
            win = (long) (mTable.getConfig().getMatchEnterFee() * (2 - commission));
        } else {
            commissionLive = (long) (TABLE_ENTRY_FEE * commission);
            win = (long) (TABLE_ENTRY_FEE * (2 - commission));
        }


        mTable.setWinner(TableInfo.Identity.Right.ordinal());//设置右边胜利
        mTable.getPlayerInfoB().setScoreAdd(win);
        mTable.getPlayerInfoB().setCommissionAdd(commissionLive);
        userScoreUpdateDB(mTable.getPlayerInfoB(), win, CHANGE_TYPE_WIN, mTable.getTableId());
        userScoreDB(mTable.getPlayerInfoB(), CHANGE_TYPE_WIN);
    }

    private void PostPlaying(TableInfo tableInfo) {
        ArrayList<UserPlayingInfo> list = new ArrayList<>();
        UserPlayingInfo playingInfoA = createUserPlayingInfo(tableInfo, tableInfo.getPlayerInfoA());
        list.add(playingInfoA);
        UserPlayingInfo playingInfoB = createUserPlayingInfo(tableInfo, tableInfo.getPlayerInfoB());
        list.add(playingInfoB);

        final String json = getJsonUserPlaying(tableInfo, list);
        System.out.println("游玩记录" + json);
        executor.submit(() -> {
            PostJsonDeal postJsonDeal = new PostJsonDeal();
            postJsonDeal.post(json, API_POST_PlayingList, PostJsonDeal.POST_TYPE_PLAYING);
        });

    }

    private UserPlayingInfo createUserPlayingInfo(TableInfo tableInfo, UserInfo user) {

        UserPlayingInfo info = new UserPlayingInfo();
        info.setUid(user.getUid());
        info.setLiveid(user.getLiveid());
        info.setGameId(tableIdIndex);//使用桌子号来标记游戏场次
        info.setGameResults(tableInfo.getWinner());//设置赢家
        long bigBet = user.getDefender() == TableInfo.Identity.Left.ordinal() ? TABLE_ENTRY_FEE : 0;
        long smallBet = user.getDefender() == TableInfo.Identity.Right.ordinal() ? TABLE_ENTRY_FEE : 0;

        info.setBigBetScoreTotal(bigBet);//押注
        info.setSmallBetScoreTotal(smallBet);//押注

        info.setReturnBigBetScore(0);//平衡后押注
        info.setReturnSmallBetScore(0);//平衡后押注

        float commission = getCommission(user);
        long winTotal = 0;
        if (tableInfo.getWinner() == user.getDefender()) {//赢家,玩家身份标识
            winTotal = (long) (TABLE_ENTRY_FEE * (2 - commission));
        }
        info.setWin(winTotal);
        info.setGameStartTime(tableInfo.getMatchStartTime());
        info.setGameEndTime(System.currentTimeMillis());

        return info;
    }

    /***
     * 包装对象为Json字符串
     * @param list 玩家游玩记录list
     * @return 返回json格式String
     */

    private String getJsonUserPlaying(TableInfo mTable, ArrayList<UserPlayingInfo> list) {
        PostUserPlaying order = new PostUserPlaying();
        order.setOrderid(mTable.getTableId());
        order.setGameid(GAME_ID);
        order.setResult(list);
        order.setAddtime(System.currentTimeMillis());
        String json = new Gson().toJson(order);
        String sign = Utils.encryptMD5(json);
        order.setSign(sign);
        return new Gson().toJson(order);
    }


    private void updatePlayerPos(UserInfo playerInfo) {
        Position uPos = new Position();
        uPos.setX(playerInfo.getDefender() == 1 ? -500 : 500);
        uPos.setY(random(-3, 3) * 20);
        playerInfo.setuPos(uPos);
    }

    private void initPlayerPos(UserInfo playerInfo) {
        Position uPos = new Position();
        uPos.setX(playerInfo.getDefender() == 1 ? -500 : 500);
        uPos.setY(-430);
        playerInfo.setuPos(uPos);
    }


    public String getFileName(int uid) {
        return uid + ".log";
    }


    void sendErrorMsg(WebSocket conn, Object cmd) {
        send(conn, GAME_MAIN, GAME_SUB_SOCKET_ERROR, cmd);
    }


    private String orderId() {
        return "game_" + Utils.getDateTableId() + "_" + tableIdIndex;
    }


    private void GetScoreFirst(WebSocket conn, String json) {
        Gson gson = new Gson();
        //获取分数
        Type type = new TypeToken<GameMessage<Login>>() {
        }.getType();//无敌好用
        GameMessage<Login> data = gson.fromJson(json, type);
        final Login login = data.getCmd();
        final int uid = login.getUid();
        webMap.put(uid, conn);
        long current = System.currentTimeMillis();
        FileUtils.writeTxtToFile("进入房间,玩家首次进入信息" + login, LogDate(), getFileName(login.getUid()));
        if (login.getLiveid() == 0) {
            FileUtils.writeTxtToFile("进入房间,玩家信息异常" + login, LogDate(), getFileName(login.getUid()));
            return;
        }
        //检查是否卡分
        SaveUserScore saveUserScore = new SaveUserScore();
        saveUserScore.addListener(new SaveUserScore.ISendListener() {
            @Override
            public void sendSuccess() {
                new Thread(() -> {

                    String json0 = gson.toJson(login);
                    System.out.println(FileUtils.getTime() + data + ",请求地址" + API_GET_COIN + "数据" + json0);

                    String result = doPostForm(API_GET_COIN, json0);
                    //获取失败为空，错误信息
                    if (result.isEmpty()) {
                        FileUtils.writeTxtToFile("进入房间,获取账户分数失败" + login, LogDate(), getFileName(login.getUid()));
                        JsonBean<String> cmd = new JsonBean<>();
                        cmd.code = GAME_C_USER_GET_MONEY_ERROR;
                        cmd.msg = "Failed to obtain score";
                        sendErrorMsg(conn, cmd);
                        return;
                    }
                    //获取失败没有该用户，错误信息
                    Type json_bean_type = new TypeToken<JsonBean>() {
                    }.getType();
                    JsonBean json_bean = gson.fromJson(result, json_bean_type);
                    if (json_bean.code != 0) {
                        FileUtils.writeTxtToFile("进入房间,获取账户分数失败" + login + json_bean, LogDate(), getFileName(login.getUid()));
                        JsonBean cmd = new JsonBean();
                        cmd.code = json_bean.code;
                        cmd.msg = "Failed to obtain score,code=" + json_bean.code;
                        sendErrorMsg(conn, cmd);
                        return;
                    }

                    //正常获取用户信息
                    Type type0 = new TypeToken<JsonBean<UserLoginInfo>>() {
                    }.getType();

                    System.out.println("获取用户信息" + result);
                    JsonBean<UserLoginInfo> bean = gson.fromJson(result, type0);
                    UserLoginInfo userLoginInfo = bean.data;
                    //创建原始金额信息
                    Money money = new Money();
                    money.setDiamond(userLoginInfo.getDiamond());
                    money.setCoin(userLoginInfo.getCoin());

                    System.out.println(FileUtils.getTime() + bean);
                    FileUtils.writeTxtToFile("获取分数," + bean, LogDate(), getFileName(login.getUid()));

                    if (bean.code == 0) {
                        UserInfo userInfo = new UserInfo();
                        userInfo.setUid(login.getUid());
                        userInfo.setMac(login.getToken());
                        userInfo.setLiveid(login.getLiveid());
                        userInfo.setStream(login.getStream());
                        userInfo.setMoney(money);//保存原始金额
                        userInfo.setUserLoginInfo(userLoginInfo);
                        userInfo.setName(userLoginInfo.getUser_nickname());
                        userInfo.setScore(getScore(money));
                        userInfo.setStatus(USER_STATUS_ONLINE);
                        userInfo.setTableId(TABLE_DEFAULT_ID);
                        if (userMap.get(login.getUid()) != null) {
                            return;
                        }
                        userMap.put(login.getUid(), userInfo);
                        livePKMgr.removeSpectator(login.getUid());

                        //TODO 保存玩家登录分数,防止服务器异常关闭导致分数丢失
                        final String jsonWriteScore = userScoreUpdateJson(userInfo);
                        saveUserScore.saveUserScore(userInfo.getUid() + "", jsonWriteScore, API_POST_WriteScore, CHANGE_TYPE_LOGIN);

                        userScoreUpdateDB(userInfo, getScore(money), CHANGE_TYPE_LOGIN, "ArrowPlaza");
                        send(getConn(userInfo), GAME_MAIN, GAME_C_SUB_ENTER_ROOM_RETURN, userInfo);
                        sendMatchLivePkInfo(login);
                        sendMatchInfo(login, userInfo);
                    } else {
                        sendErrorMsg(conn, bean.data);
                    }
                }).start();

            }

            @Override
            public void sendError() {

            }
        });
        saveUserScore.checkDBUserScore(uid + "");
        printTime("GetScoreFirst", current);
    }

    /**
     * 玩家分数变更日志
     *
     * @param userInfo    玩家信息
     * @param changeScore 变动分数
     * @param changeType  变动类型
     * @param location    变动位置
     */
    private void userScoreUpdateDB(UserInfo userInfo, long changeScore, String changeType, String location) {
        UserScoreUpdate userScoreUpdate = new UserScoreUpdate();
        userScoreUpdate.setUid(userInfo.getUid());
        userScoreUpdate.setCurrentScore(userInfo.getScore());
        userScoreUpdate.setChangeType(changeType);
        userScoreUpdate.setLocation(location);
        userScoreUpdate.setScore(userInfo.getScore() - changeScore);
        userScoreUpdate.setChangeScore(changeScore);
        userScoreUpdate.setRemark(userInfo.getMoney().toString());
        userScoreUpdate.setCreateTime(Utils.getTime());
        executor.submit(() -> UserScoreDaoImpl.getInstance().addUserScoreUpdate(userScoreUpdate));


    }

    /*** 玩家实时状态记录
     *
     * @param userInfo 玩家信息
     * @param desc 变动类型
     */
    private void userScoreDB(UserInfo userInfo, String desc) {
        executor.submit(() -> UserScoreDaoImpl.getInstance().updateUserScore(userInfo.getUid() + "", userScoreUpdateJson(userInfo), desc));
    }

    /*** 离开平台时状态记录
     *
     * @param userInfo 玩家信息
     * @param desc 退出类型
     */
    private void userExit(UserInfo userInfo, String desc) {
        executor.submit(() -> {
            long c = System.currentTimeMillis();
            UserScoreDaoImpl.getInstance().userExit(userInfo.getUid() + "", desc);
            printTime("userExit", c);
        });
    }

    /*** 离开平台时状态记录 批量处理
     *
     * @param userInfoList 玩家信息
     * @param jsonWriteScore 退出类型
     */
    private void userExitBatch(List<UserInfo> userInfoList, String jsonWriteScore) {
        executor.submit(() -> {
            long c = System.currentTimeMillis();
            UserScoreDaoImpl.getInstance().userExitBatch(userInfoList, DESC_SYSTEM_EXIT);
            printTime("userExitBatch", c);
            PostJsonDeal postJsonDeal = new PostJsonDeal();
            postJsonDeal.addListener(new PostJsonDeal.ISendListener() {
                @Override
                public void sendSuccess() {
                    GameMessage<String> cmd = new GameMessage<>();
                    cmd.setMain(GAME_MAIN);
                    cmd.setSub(GAME_SUB_SOCKET_ERROR);
                    cmd.setCmd("onClose");
                    String json = new Gson().toJson(cmd);
                    broadcast(json);
                    printTime("post", c);
                    System.out.println("退出");
                    System.exit(0);
                }

                @Override
                public void error(String e) {

                }
            });
            postJsonDeal.post(jsonWriteScore, API_POST_WriteScore, DESC_SYSTEM_EXIT);

        });
        executor.shutdown();


    }

    /*** 玩家退出，提交分数给总服务器,进行退出
     *
     * @param jsonWriteScore 提交信息
     * @param desc 退出类型
     */
    private void postExit(String jsonWriteScore, String desc) {
        executor.submit(() -> {
            PostJsonDeal postJsonDeal = new PostJsonDeal();
            postJsonDeal.post(jsonWriteScore, API_POST_WriteScore, desc);
        });
    }


    private void sendPlayerImageHead(UserInfo playerInfo, String base64Image) {
        //发送这个玩家的头像信息
        PlayerHeadImage playerHeadImage = new PlayerHeadImage();
        playerHeadImage.uid = playerInfo.getUid();
        playerHeadImage.headImageBase64 = base64Image;
        playerHeadImage.niceName = playerInfo.getUserLoginInfo().getUser_nickname();
        playerHeadImage.score = playerInfo.getScore();
        playerHeadImage.defender = playerInfo.getDefender();
        send(getConn(playerInfo), GAME_MAIN, GAME_C_SUB_USER_HEAD_INFO_RETURN, playerHeadImage);

    }

    /***
     *   发送比赛双方的头像给参赛者
     * @param mTable 本桌信息
     */
    private void sendMatchPlayerImageHead(TableInfo mTable) {

        String base64ImageA = getImageHeadBase64(mTable.getPlayerInfoA());
        String base64ImageB = getImageHeadBase64(mTable.getPlayerInfoB());


        if (!base64ImageA.isEmpty() && !base64ImageB.isEmpty()) {
            MatchImageHead matchImageHead = new MatchImageHead();
            matchImageHead.setTableId(mTable.getTableId());
            matchImageHead.imageHeadBase64A = base64ImageA;
            matchImageHead.imageHeadBase64B = base64ImageB;
            matchImageHead.nickNameA = mTable.getPlayerInfoA().getUserLoginInfo().getUser_nickname();
            matchImageHead.nickNameB = mTable.getPlayerInfoB().getUserLoginInfo().getUser_nickname();

            livePKMgr.getImageHeadMap().put(matchImageHead.getTableId(), matchImageHead);

            send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_HEAD_INFO_RETURN, matchImageHead);
            send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_HEAD_INFO_RETURN, matchImageHead);

            System.out.println("发送头像信息");
        }
    }


    public void initGameConfig(GameConfig gameConfig) {
        this.gameConfig = gameConfig;
    }

    public GameConfig getGameConfig() {
        return this.gameConfig;
    }

    /**
     * 获取总服务器配置信息,每局都去获取
     */
    private void initConfig() {
        FileUtils.writeTxtToFile("获取配置,请求地址:" + Score.API_GET_CONFIG, Utils.LogDate(), LOG_NAME_ROOM);

        new Thread(() -> {
            String result = doPostForm(API_GET_CONFIG, "");

            if (result.isEmpty()) {
                System.out.println("获取配置:失败");
                return;
            }
            Type type0 = new TypeToken<JsonBean<GameConfig>>() {
            }.getType();

            JsonBean<GameConfig> bean = new Gson().fromJson(result, type0);

            GameConfig gameConfig = bean.data;
            //  System.out.println("获取配置:" + result + bean);
            if (gameConfig != null) {
                FileUtils.writeTxtToFile("获取配置,获取成功:" + gameConfig, Utils.LogDate(), LOG_NAME_ROOM);
                initGameConfig(gameConfig);

            }

        }).start();
    }


    public long getScore(Money money) {
        if (gameConfig == null) {
            return 0;
        }
        return money.getCoin() + money.getDiamond() * gameConfig.getDiamondToCoinRatio();
    }

    /***
     * 加入房间,普通玩家快速匹配进入的房间
     * @param uid 用户id
     */
    public boolean joinMatch(int uid) {
        UserInfo player = getUser(uid);//赋值
        if (!player.getTableId().isEmpty()) {//表示已经加入了桌子,此时应该返回信息可
            TableInfo mTable = tableMap.get(player.getTableId());
            send(getConn(mTable.getPlayerInfoA()), 200, GAME_C_SUB_MATCH_UP_RETURN, mTable);
            send(getConn(mTable.getPlayerInfoB()), 200, GAME_C_SUB_MATCH_UP_RETURN, mTable);//发送信息給自己
            return true;
        }

        for (String key : tableMap.keySet()) {
            TableInfo mTable = tableMap.get(key);
            //  System.out.println("Key = " + key + ", Value = " + tableInfo);
            if (mTable.getLivePK() == null//确保不是主播PK创建的房间
                    && mTable.getTableStatus() == TableInfo.TableState.ACreate.ordinal() && mTable.getPlayerInfoB() == null) {//如果有桌子并且有座位,直接加入(位置在右边)
                player.setDefender(TableInfo.Identity.Right.ordinal());//设置坐在右边
                player.setTableId(key);//设置房间ID

                updatePlayerPos(player);
                player.setHp(GameDefine.PLAYER_HP);
                Weapon weapon = new Weapon();
                weapon.arrowIndex = 0;
                weapon.angle = 180;
                weapon.force = 0;
                weapon.status = 0;
                player.setWeapon(weapon);
                player.setPrepare(UserInfo.PREPARED);//设置准备
                player.setLifes(mTable.getConfig().getMatchWinNumber());//设置生命数

                mTable.setPlayerInfoB(player); //赋值
                mTable.setTableStatus(TableInfo.TableState.BJoin.ordinal()); //锁定房间,不能再进人了
                //TODO 此处扣钱 双方都进入房间里。
                System.out.println("加入房间=" + mTable);
                mTable.getPlayerInfoA().setScoreLower(TABLE_ENTRY_FEE);
                mTable.getPlayerInfoB().setScoreLower(TABLE_ENTRY_FEE);
                mTable.setMatchStartTime(System.currentTimeMillis());//本桌比赛开始时间
//========
                userScoreUpdateDB(mTable.getPlayerInfoA(), -TABLE_ENTRY_FEE, CHANGE_TYPE_FEE, mTable.getTableId());
                userScoreUpdateDB(mTable.getPlayerInfoB(), -TABLE_ENTRY_FEE, CHANGE_TYPE_FEE, mTable.getTableId());

                userScoreDB(mTable.getPlayerInfoA(), CHANGE_TYPE_FEE);
                userScoreDB(mTable.getPlayerInfoB(), CHANGE_TYPE_FEE);

                send(getConn(mTable.getPlayerInfoA()), 200, GAME_C_SUB_MATCH_UP_RETURN, mTable);
                send(getConn(mTable.getPlayerInfoB()), 200, GAME_C_SUB_MATCH_UP_RETURN, mTable);

                sendMatchPlayerImageHead(mTable);//单独发送头像图片
                if (mTable.getMatchDuration() == MATCH_DURATION) {
                    //发送游戏开始
                    send(getConn(mTable.getPlayerInfoA()), GAME_MAIN, GAME_C_SUB_MATCH_GAME_START_RETURN, mTable);
                    send(getConn(mTable.getPlayerInfoB()), GAME_MAIN, GAME_C_SUB_MATCH_GAME_START_RETURN, mTable);
                }

                return true;
            }
        }
        return false;
    }


    public int random(int min, int max) {
        Random random = new Random();
        return random.nextInt(max - min + 1) + min;
    }

    /**
     * 单个玩家离开游戏，结算
     *
     * @param userInfo 玩家信息
     */
    private void onUserExitGame(UserInfo userInfo) {
        //TODO 如果桌子状态是
        TableInfo tableInfo = tableMap.get(userInfo.getTableId());
        if (tableInfo != null && tableInfo.getMatchDuration() == MATCH_DURATION) {
            //此时桌子未开始游戏，先离开桌子再进行结算
            exitTable(userInfo.getTableId(), userInfo.getUid());
        }

        userInfo.setStatus(USER_STATUS_EXIT);//主动离开
        //  System.out.println("主动离开,玩家信息= " + userInfo);

        GamesWriteScore writeScore = getGamesWriteScore(userInfo);

        ArrayList<GamesWriteScore> writeScoreList = new ArrayList<>();
        writeScoreList.add(writeScore);

        final String jsonWriteScore = getJsonWriteScore(writeScoreList);
        FileUtils.writeTxtToFile("主动离开,离开时个人信息" + userInfo, LogDate(), getFileName(userInfo.getUid()));
        FileUtils.writeTxtToFile("主动离开,发往总服务器" + jsonWriteScore, LogDate(), LOG_NAME_ROOM);
        long exitScore = -userInfo.getScore();

        userInfo.setScore(0);
        userInfo.getMoney().setCoin(0);
        userInfo.getMoney().setDiamond(0);
        userInfo.setCommission(0);


        userMap.entrySet().removeIf(item -> exitCheck(item.getValue()));//清除这个玩家

        userScoreUpdateDB(userInfo, exitScore, CHANGE_TYPE_EXIT, "ArrowPlaza");
        userExit(userInfo, SaveUserScore.DESC_NORMAL_EXIT);
        postExit(jsonWriteScore, CHANGE_TYPE_EXIT + userInfo.getUid());


    }

    /**
     * 服务器关闭 ，全体结算
     */
    private void onSystemExitGame() {
        if (isGamesWriteScore) {//确保只发送一次
            return;
        }
        isGamesWriteScore = true;

        ArrayList<GamesWriteScore> writeScoreList = new ArrayList<>();
        userMap.forEach((key, userInfo) -> {
            if (!userInfo.getMac().equals(ROOM_ROBOT_MAC)) {
                GamesWriteScore writeScore = getGamesWriteScore(userInfo);
                writeScoreList.add(writeScore);
            }

        });
        if (writeScoreList.isEmpty()) {
            System.exit(0);
            return;
        }

        final String jsonWriteScore = getJsonWriteScore(writeScoreList);
        FileUtils.writeTxtToFile("服务器关闭,发往总服务器" + jsonWriteScore, LogDate(), LOG_NAME_ROOM);
        //更新这张表里的数据,然后统一发送给服务器结算
        List<UserInfo> userInfoList = new ArrayList<>();

        userMap.forEach((key, userInfo) -> {
            if (!userInfo.getMac().equals(ROOM_ROBOT_MAC)) {
                userInfoList.add(userInfo);
                // userExit(userInfo, DESC_SYSTEM_EXIT);
            }
        });
        userExitBatch(userInfoList, jsonWriteScore);


    }

    /***
     * 包装对象为Json字符串
     * @param writeScoreList 结算的List
     * @return 返回json格式String
     */

    private String getJsonWriteScore(ArrayList<GamesWriteScore> writeScoreList) {
        OrderUserWriteScore order = new OrderUserWriteScore();
        order.setOrderid(orderId());
        order.setGameid(GAME_ID);
        order.setResult(writeScoreList);
        order.setAddtime(System.currentTimeMillis());
        String json = new Gson().toJson(order);
        String sign = Utils.encryptMD5(json);
        order.setSign(sign);
        return new Gson().toJson(order);
    }

    private GamesWriteScore getGamesWriteScore(UserInfo user) {
        GamesWriteScore writeScore = new GamesWriteScore();
        writeScore.setUid(user.getUid());
        writeScore.setLiveid(user.getLiveid());
        writeScore.setStream(user.getStream());
        writeScore.setMoney(user.getMoney());
        Money profit;
        if (user.getPlatform() != null && user.getPlatform().equals("h5")) {
            profit = user.getUserLoginInfo().getH5SettlementType() == 0 ? getProfitMoney(user) : getProfitMoney2(user);

        } else {
            profit = user.getUserLoginInfo().getAPPSettlementType() == 0 ? getProfitMoney(user) : getProfitMoney2(user);

        }
        writeScore.setProfitMoney(profit);
        writeScore.setCommission(user.getCommission());
        return writeScore;
    }

    private Money getProfitMoney(UserInfo user) {
        Money profit = new Money();
        final long win = user.getScore() - getScore(user.getMoney());
        if (win >= 0) {
            //表示盈利
            profit.setCoin(win);//只能赢金币
            profit.setDiamond(0);//钻石没变化
        } else {

            long lose = user.getMoney().getCoin() + win;
            if (lose >= 0) {
                profit.setCoin(win);//只输金币,此时win为负数
                profit.setDiamond(0);//钻石没变化
            } else {
                long diamond = user.getScore() / gameConfig.getDiamondToCoinRatio();//转钻石
                long coin = user.getScore() % gameConfig.getDiamondToCoinRatio();//余下的为金币
                long loseDiamond = diamond - user.getMoney().getDiamond();
                long loseCoin = coin - user.getMoney().getCoin();

                profit.setCoin(loseCoin);//金币输了,
                profit.setDiamond(loseDiamond);//钻石输了
            }
        }
        return profit;
    }

    /***
     * 模式二,赢分优先转钻石
     * @param user 传入玩家初始信息
     * @return 返回赢分
     */
    private Money getProfitMoney2(UserInfo user) {
        Money profit = new Money();
        final long win = user.getScore() - getScore(user.getMoney());
        if (win >= 0) {
            //表示盈利
            long mProfitCoin = win % gameConfig.getDiamondToCoinRatio();
            long mProfitDiamond = win / gameConfig.getDiamondToCoinRatio();

            profit.setCoin(mProfitCoin);//金币
            profit.setDiamond(mProfitDiamond);//钻石
        } else {

            long lose = user.getMoney().getCoin() + win;
            if (lose >= 0) {
                profit.setCoin(win);//只输金币,此时win为负数
                profit.setDiamond(0);//钻石没变化
            } else {
                long diamond = user.getScore() / gameConfig.getDiamondToCoinRatio();//转钻石
                long coin = user.getScore() % gameConfig.getDiamondToCoinRatio();//余下的为金币
                long loseDiamond = diamond - user.getMoney().getDiamond();
                long loseCoin = coin - user.getMoney().getCoin();

                profit.setCoin(loseCoin);//金币输了,
                profit.setDiamond(loseDiamond);//钻石输了
            }
        }
        return profit;
    }

    /**
     * 如果是机器人则不抽水
     */
    private float getCommission(UserInfo user) {
        return user.getMac().equals(ROOM_ROBOT_MAC) ? 0 : gameConfig.getBarrageGameRatio() / 100.0f;
    }

    /***
     * 更新用户的武器信息
     * @param userWeapon 服务器里保存的原始信息
     * @param weapon 用户发送的实时信息
     */
    private void updateWeapon(Weapon userWeapon, Weapon weapon) {

        userWeapon.force = weapon.force;
        userWeapon.angle = weapon.angle;
        userWeapon.status = weapon.status;
    }

    public void createLivePKTable(CreateLivePK livePK) {

        if (isCreated(livePK)) {
            return;
        }
        TableInfo mTable = new TableInfo();//创建桌子
        tableIdIndex++;//tableIdIndexId累加
        String tableId = Utils.getDateTableId() + tableIdIndex;
        mTable.setTableId(tableId);
        mTable.setTableStatus(TableInfo.TableState.ACreate.ordinal());
        mTable.setWinner(TableInfo.Identity.Default.ordinal());
        mTable.setMatchDuration(MATCH_DURATION);
        mTable.setOnDestroyTime(DESTROY_TIME);
        TableConfig config = new TableConfig();
        config.setMatchWinNumber(livePK.getMatchWinNumber());
        config.setMatchDuration(MATCH_DURATION);
        config.setMatchEnterFee(livePK.getMatchEnterFee());
        mTable.setConfig(config);
        mTable.setLivePK(livePK);

        tableMap.put(tableId, mTable);//添加房间
        System.out.println("主播创建桌子=" + mTable);

        if (mTable.getLivePK() != null && mTable.getTableStatus() == TableInfo.TableState.ACreate.ordinal()) {
            //TODO 直播间创建的比赛,发送给对应的玩家登录消息
            UserInfo playerA = getUser(mTable.getLivePK().getUidA());//
            send(getConn(playerA), GAME_MAIN, GAME_C_SUB_CREATE_LIVE_PK_MATCH_RETURN, mTable);

            UserInfo playerB = getUser(mTable.getLivePK().getUidB());//
            send(getConn(playerB), GAME_MAIN, GAME_C_SUB_CREATE_LIVE_PK_MATCH_RETURN, mTable);
            System.out.println("GAME_C_SUB_CREATE_LIVE_PK_MATCH_RETURN=" + mTable);

        }


    }

    private boolean isCreated(CreateLivePK livePK) {
        AtomicBoolean isCreated = new AtomicBoolean(false);
        //TODO 先判断是否有相同的比赛信息
        for (Map.Entry<String, TableInfo> entry : tableMap.entrySet()) {
            TableInfo table = entry.getValue();
            if (table.getLivePK() != null) {
                boolean bA = livePK.getUidA() == table.getLivePK().getUidA();
                boolean bB = livePK.getUidB() == table.getLivePK().getUidB();
                if (bA && bB) {//如果已经有比赛则返回
                    System.out.println("已创建相同LivePK比赛," + table);
                    isCreated.set(true);
                    break;
                }

            }

        }
        return isCreated.get();
    }

    public WebSocket getConn(UserInfo userInfo) {
        if (userInfo != null) {
            return webMap.get(userInfo.getUid());
        }
        return null;
    }


    public void joinLivePKMatch(String json) {
        Type type = new TypeToken<GameMessage<MatchUp>>() {
        }.getType();//无敌好用

        GameMessage<MatchUp> data = new Gson().fromJson(json, type);
        MatchUp matchUp = data.getCmd();//玩家发起匹配。

        TableInfo mTable = tableMap.get(matchUp.getTableId());

        if (mTable == null) {
            return;
        }
        if (mTable.getTableStatus() == TableInfo.TableState.Destroy.ordinal()) {
            //比赛结束
            return;
        }
        // TODO 先判断主播是否有未完成的比赛,
        UserInfo user = getUser(matchUp.getUid());//添加房主
        if (user != null && user.getTableId().isEmpty() && matchUp.getUid() == mTable.getLivePK().getUidA()) {
            user.setDefender(TableInfo.Identity.Left.ordinal());//设置为房主-左边
            initUser(user, mTable);
            mTable.setPlayerInfoA(user);//添加房主
            send(getConn(user), GAME_MAIN, GAME_C_SUB_JOIN_LIVE_PK_MATCH_RETURN, mTable);
        }
        if (user != null && user.getTableId().isEmpty() && matchUp.getUid() == mTable.getLivePK().getUidB()) {
            user.setDefender(TableInfo.Identity.Right.ordinal());//设置为右边
            initUser(user, mTable);
            mTable.setPlayerInfoB(user);
            send(getConn(user), GAME_MAIN, GAME_C_SUB_JOIN_LIVE_PK_MATCH_RETURN, mTable);
        }

        if (mTable.getPlayerInfoA() != null && mTable.getPlayerInfoB() != null) {
            sendMatchPlayerImageHead(mTable); //两人都准备了则发送比赛头像
            mTable.setTableStatus(TableInfo.TableState.BJoin.ordinal());//修改桌子状态为加入

        }


    }

    private void initUser(UserInfo user, TableInfo mTable) {

        user.setTableId(mTable.getTableId());//设置房间ID
        updatePlayerPos(user);
        user.setHp(GameDefine.PLAYER_HP);
        Weapon weapon = new Weapon();
        weapon.arrowIndex = 0;
        weapon.angle = user.getDefender() == 1 ? 0 : 180;
        weapon.force = 0;
        weapon.status = 0;
        user.setWeapon(weapon);//初始化武器
        user.setPrepare(UserInfo.PREPARED);//设置准备
        user.setLossHp(0);
        user.setLifes(mTable.getConfig().getMatchWinNumber());//设置生命数
    }

    /***
     * 检查是否有直播PK,
     * @param login 登录信息
     */
    private void sendMatchLivePkInfo(Login login) {
        if (login.getIsLoadImageHead() == 0) {
            tableMap.forEach((key, mTable) -> {

                if (mTable.getLivePK() != null && mTable.getTableStatus() == TableInfo.TableState.ACreate.ordinal()) {
                    //TODO 直播间创建的比赛,发送给对应的玩家登录消息
                    UserInfo playerA = getUser(mTable.getLivePK().getUidA());//
                    send(getConn(playerA), GAME_MAIN, GAME_C_SUB_CREATE_LIVE_PK_MATCH_RETURN, mTable);

                    UserInfo playerB = getUser(mTable.getLivePK().getUidB());//
                    send(getConn(playerB), GAME_MAIN, GAME_C_SUB_CREATE_LIVE_PK_MATCH_RETURN, mTable);
                    System.out.println("GAME_C_SUB_CREATE_LIVE_PK_MATCH_RETURN=" + mTable);

                }

            });

        }
    }

    private boolean exitCheck(UserInfo user) {
        //玩家断开连接,没有进入任何桌子则可以移除这个玩家的登录信息,否则保留
        return user.getStatus() == USER_STATUS_EXIT && user.getTableId().isEmpty();
    }

    public void printTime(String name, long time) {
        String hs = (System.currentTimeMillis() - time) + "ms";
        System.out.printf("%s耗时:%s\n", name, hs);
    }

}


